#pragma once
#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/TcpConnection.h>
#include <muduo/net/Buffer.h>
#include <muduo/base/CountDownLatch.h>
#include <muduo/net/EventLoopThread.h>
#include <muduo/net/TcpClient.h>
#include <iostream>
#include <unordered_map>
#include <functional>
#include "message.hpp"
#include <arpa/inet.h>
#include <mutex>
#include <climits>
using namespace ns_msg;

namespace ns_net
{
    // 里面取出来的东西都已经是主机字节序了！！！
    class MuduoBuffer : public BaseBuffer
    {
    public:
        using ptr = std::shared_ptr<MuduoBuffer>;
        MuduoBuffer(muduo::net::Buffer *buf) : _buf(buf) {}
        virtual size_t Readablebytes() override
        {
            return _buf->readableBytes();
        }
        virtual int32_t Peekint32() override
        {
            return _buf->peekInt32();
        }
        virtual void Retrieveint32() override
        {
            _buf->retrieveInt32();
        }
        virtual int32_t Readint32() override
        {
            return _buf->readInt32();
        }
        virtual std::string RetrieveAsstring(size_t len) override
        {
            return _buf->retrieveAsString(len);
        }

    private:
        muduo::net::Buffer *_buf;
    };

    class BufferFactory
    {
    public:
        template <class... Args>
        static BaseBuffer::ptr ProtocolCreate(Args &&...args)
        {
            return std::make_shared<MuduoBuffer>(std::forward<Args>(args)...);
        }
    };

    class LVProtocol : public BaseProtocol
    {
    public:
        using ptr = std::shared_ptr<LVProtocol>;

        // |--Len--|--Val--|
        virtual bool Canprocessed(const BaseBuffer::ptr &buf) override
        {
            // 因为客户端的回调onMessage是个死循环，缓冲区被拿走之后，还会判断，所以缓冲区数据为0会直接走这里false，所以不是bug，是合理的判断机制
            if (buf->Readablebytes() < LenFieldsLenth)
            {
                LOG(INFO, "buf->Readablebytes(): %d", buf->Readablebytes());
                //LOG(ERROR, "缓冲区数据甚至不够4字节");
                return false;
            }
            int32_t total_len = buf->Peekint32() + LenFieldsLenth;
            LOG(INFO, "total_len : %d", total_len);
            // 不足一条完整的数据
            if (buf->Readablebytes() < total_len)
            {
                LOG(ERROR, "缓冲区不足一条完整的数据");
                return false;
            }
            return true;
        }
        // |--Len--|--Mtype--|--Idlen--|----Id----|------Body-----|
        // 取数据
        virtual bool Onmessage(const BaseBuffer::ptr &buf, BaseMessage::ptr &msg) override
        {
            // 这里的实现，默认已经是完整的数据了，用户会自己用Canprocessed判断

            // total_len 是从 Len 字段开始到消息末尾的总长度，因此不需要再次减去 Len 字段的长度
            int32_t total_len = buf->Readint32() + LenFieldsLenth;
            Mtype mtype = (Mtype)buf->Readint32();
            int32_t id_len = buf->Readint32();
            int32_t body_len = total_len - LenFieldsLenth - MtypeFieldsLenth - IdlenFieldsLenth - id_len;
            std::string id = buf->RetrieveAsstring(id_len);
            std::string body = buf->RetrieveAsstring(body_len);
            // 根据buf里的内容构造msg对象，直接用参数里的msg即可
            msg = MessageFactory::Createmsg(mtype);
            // std::shared_ptr<T>::get 成员函数的作用是返回它所管理的原始指针（即类型为 T* 的指针），该指针指向 std::shared_ptr 所拥有的对象。如果 std::shared_ptr 是空的（即不指向任何对象），则 get 将返回 nullptr
            if (msg.get() == nullptr)
            {
                LOG(ERROR, "消息类型错误，构造消息对象失败!!!");
                return false;
            }
            // 用户还需要在msg里拿到结构化字段,unserialize会把结构化字段放到里面的_val里。
            bool ret = msg->unserialize(body);
            if (ret == false)
            {
                LOG(ERROR, "消息正文反序列化失败!!!");
                return false;
            }
            msg->Setid(id);
            msg->SetMtype(mtype);
            return true;
        }
        virtual std::string Serialize(const BaseMessage::ptr &msg) override
        {
            // 序列化的东西需要主机转网络字节序
            // muduo库的Readint32就是从接收缓冲区读数据，内部已经实现了网络转主机，所以我们不用亲自实现s
            std::string body = msg->serialize();
            std::string id = msg->Getid();
            int32_t id_len = htonl(id.size());
            auto mtype = htonl((int32_t)msg->GetMtype());
            int32_t h_total_len = body.size() + id.size() + IdlenFieldsLenth + MtypeFieldsLenth;
            LOG(INFO, "h_total_len: %d", h_total_len);
            int32_t n_total_len = htonl(h_total_len);
            std::string result;
            result.reserve(h_total_len); // 避免append多次内存申请

            // 网络字节序
            result.append((char *)&n_total_len, LenFieldsLenth);
            result.append((char *)&mtype, MtypeFieldsLenth);
            result.append((char *)&id_len, IdlenFieldsLenth);
            result.append(id);
            result.append(body);

            return result;
        }

    private:
        const size_t LenFieldsLenth = 4;
        const size_t MtypeFieldsLenth = 4;
        const size_t IdlenFieldsLenth = 4;
    };

    class ProtocolFactory
    {
    public:
        template <class... Args>
        static BaseProtocol::ptr ProtocolCreate(Args &&...args)
        {
            return std::make_shared<LVProtocol>(std::forward<Args>(args)...);
        }
    };

    class MuduoConnection : public BaseConnection
    {
    public:
        using ptr = std::shared_ptr<MuduoConnection>;
        MuduoConnection(const muduo::net::TcpConnectionPtr &conn, const BaseProtocol::ptr &protocol)
            : _protocol(protocol), _conn(conn)
        {
        }
        // BaseMessage本身并不序列反序列化，在MuduoConnection时发数据才需要序列化
        virtual void Send(const BaseMessage::ptr &msg) override
        {
            std::string ret = _protocol->Serialize(msg);
            // if (ret.c_str() == nullptr)
            //     LOG(ERROR, "序列化的字符串为空!!!");
            _conn->send(ret);
        }
        virtual void shutdown() override
        {
            _conn->shutdown();
        }
        virtual bool connected() override
        {
            return _conn->connected();
        }

    private:
        BaseProtocol::ptr _protocol;
        muduo::net::TcpConnectionPtr _conn;
    };

    class ConnectionFactory
    {
    public:
        template <class... Args>
        static BaseConnection::ptr ConnectionCreate(Args&& ...args)
        {
            return std::make_shared<MuduoConnection>(std::forward<Args>(args)...);
        }
    };

    class MuduoServer : public BaseServer
    {
    private:
        // 假设最大数据长度为64KB 也就是65536个字节
        const size_t Max_Data = (1 << 16);
        BaseProtocol::ptr _protocol;
        muduo::net::EventLoop _baseloop;
        muduo::net::TcpServer _server;
        std::mutex _mutex;
        std::unordered_map<muduo::net::TcpConnectionPtr, BaseConnection::ptr> _hashconns;

    public:
        using ptr = std::shared_ptr<MuduoServer>;
        MuduoServer(int port)
            : _server(&_baseloop, muduo::net::InetAddress("0.0.0.0", port), "MuduoServer", muduo::net::TcpServer::kNoReusePort), _protocol(ProtocolFactory::ProtocolCreate())
        {
        }
        virtual void start() override
        {
            // 调用我写的回调
            _server.setConnectionCallback(std::bind(&MuduoServer::onConnection, this, std::placeholders::_1));
            _server.setMessageCallback(std::bind(&MuduoServer::onMessageConn, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
            _server.start();  // 开启监听
            _baseloop.loop(); // 开启循环
        }

    private:
        void onConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            if (conn->connected())
            {
                std::cout << "连接成功！" << std::endl;
                // 把连接对象存入hash
                auto muduo_conn = ConnectionFactory::ConnectionCreate(conn, _protocol);
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    _hashconns.insert(std::make_pair(conn, muduo_conn));
                }
                if (_cb_connect)
                    _cb_connect(muduo_conn);
            }
            else
            {
                // 首先，代码需要确保它有一个有效的连接对象引用（即muduo_conn），以便在删除操作之后仍然可以访问和使用这个对象（如果必要的话）。
                // 然后，一旦确认了这个引用是有效的，并且后续代码不再需要从_hashconns中访问这个连接对象，就可以安全地从容器中删除它。
                std::cout << "连接失败" << std::endl;
                BaseConnection::ptr muduo_conn;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _hashconns.find(conn);
                    if (it == _hashconns.end())
                        return;

                    muduo_conn = it->second;
                    _hashconns.erase(conn);
                }
                if (_cb_connect)
                    _cb_connect(muduo_conn);
            }
        }

        void onMessageConn(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buff, muduo::Timestamp receiveTime)
        {
            LOG(INFO, "有数据到来，开始处理");
            // 先判断是不是一条完整数据
            auto buf = BufferFactory::ProtocolCreate(buff);
            while (1)
            {
                if (_protocol->Canprocessed(buf) == false)
                {
                    // 不是完整数据
                    // 特判，会不会数据过大，导致读取错误
                    if (buf->Readablebytes() > Max_Data)
                    {
                        conn->shutdown();
                        LOG(ERROR, "缓冲区数据过大");
                        return;
                    }
                    LOG(WARNNING, "缓冲区数据过少");
                    break;
                }
                LOG(INFO, "缓冲区数据量合法");
                // 让_protocol处理数据
                BaseMessage::ptr msg;
                bool r = _protocol->Onmessage(buf, msg);
                if (r == false)
                {
                    conn->shutdown();
                    LOG(ERROR, "缓冲区数据错误")
                    return;
                }
                LOG(INFO, "数据反序列化成功，用户可以拿到结构化字段");
                // 获取conn对象，因为下面要构建回调_cb_message
                // 确认它是否仍然存在于我们的连接管理容器_hashconns中
                BaseConnection::ptr base_conn;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _hashconns.find(conn);
                    if (it == _hashconns.end())
                    {
                        conn->shutdown();
                        return;
                    }
                    base_conn = it->second;
                }
                if (_cb_message)
                    _cb_message(base_conn, msg);
            }
        }
    };

    class ServerFactory
    {
    public:
        template <class... Args>
        static BaseServer::ptr ServerCreate(Args &&...args)
        {
            return std::make_shared<MuduoServer>(std::forward<Args>(args)...);
        }
    };

    class MuduoClient : public BaseClient
    {
    private:
        const size_t Max_Data = (1 << 16);
        std::mutex _mutex;
        BaseProtocol::ptr _protocol;
        BaseConnection::ptr _conn;
        muduo::CountDownLatch _cd;
        muduo::net::EventLoopThread _loopthread; // 实例化好，内部自动循环监控，不需要手动写。
        muduo::net::EventLoop *_baseloop;
        muduo::net::TcpClient _client;

    public:
        using ptr = std::shared_ptr<MuduoClient>;
        MuduoClient(const std::string &sip, int port)
            : _client(_baseloop, muduo::net::InetAddress(sip, port), "MuduoClient"), _baseloop(_loopthread.startLoop()), _cd(1), _protocol(ProtocolFactory::ProtocolCreate())
        {
        }
        // 用户调用connect获取连接时，才需要调用下面的两个回调
        virtual void connect() override
        {
            LOG(INFO, "设置回调函数，连接服务器");
            // 调用我写的回调
            _client.setConnectionCallback(std::bind(&MuduoClient::onConnection, this, std::placeholders::_1));
            _client.setMessageCallback(std::bind(&MuduoClient::onMessageConn, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            _client.connect();
            _cd.wait();

            LOG(INFO, "连接服务器成功");
        }
        virtual bool send(const BaseMessage::ptr &msg) override
        {
            // 为什么要用类内的connected?
            if (connected() == false)
            {
                LOG(ERROR, "连接已断开！");
                return false;
            }
            _conn->Send(msg);
            return true;
        }
        virtual void shutdown() override
        {
            _client.disconnect();
        }
        virtual BaseConnection::ptr connection() override
        {
            return _conn;
        }
        virtual bool connected() override
        {
            return (_conn && _conn->connected());
        }

    private:
        void onConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            LOG(INFO, "Client进入OnConnection");
            if (conn->connected())
            {
                LOG(INFO, "连接成功");
                //这里的顺序真奇怪，到底是先还是后？
                _conn = ConnectionFactory::ConnectionCreate(conn, _protocol);
                _cd.countDown(); // 计数--，为0就阻塞

               
            }
            else
            {
                LOG(INFO, "连接失败");
                _conn.reset();
            }
        }

        void onMessageConn(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buff, muduo::Timestamp receiveTime)
        {
            LOG(INFO, "Client进入onMessage");
            // 先判断是不是一条完整数据
            auto buf = BufferFactory::ProtocolCreate(buff);

            while (1)
            {
                if (_protocol->Canprocessed(buf) == false)
                {
                    // 不是完整数据
                    // 特判，会不会数据过大，导致读取错误
                    if (buf->Readablebytes() > Max_Data)
                    {
                        conn->shutdown();
                        LOG(ERROR, "缓冲区数据过大");
                        return;
                    }
                    break;
                }
                LOG(INFO, "缓冲区数据量合法");

                // 让_protocol处理数据
                BaseMessage::ptr msg;
                bool r = _protocol->Onmessage(buf, msg);
                if (r == false)
                {
                    conn->shutdown();
                    LOG(ERROR, "缓冲区数据错误")
                    return;
                }
                //LOG(INFO, "缓冲区数据解析完毕,等待回调函数处理");

                // 客户端只有_conn对象
                if (_cb_message)
                    _cb_message(_conn, msg);
            }
        }
    };

    class ClientFactory
    {
    public:
        template <class... Args>
        static BaseClient::ptr ClientCreate(Args &&...args)
        {
            return std::make_shared<MuduoClient>(std::forward<Args>(args)...);
        }
    };
}
